#include "npcap_monitor.h"
#include "npcap_monitor.h"
#include "ui_npcap_monitor.h"
#include <QDebug>
#include <QHeaderView>
#include <QTableWidgetItem>
#include <QMutexLocker>
npcap_monitor::npcap_monitor(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::npcap_monitor)
{
    ui->setupUi(this);
    alldevs = new pcap_if_t;
    d = new pcap_if_t;
    table_devs = new QTableWidget(20,3,this);
    ui->ui_table_layout->addWidget(table_devs);
    table_devs->resize(900,350);
    connect(table_devs,&QTableWidget::cellClicked,this,&npcap_monitor::on_ui_table_cellClicked);
    // --------------------------------------------
    // table auto size
    table_devs->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
//    table_devs->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch);
    table_devs->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
    table_devs->resizeColumnsToContents();
    table_devs->resizeRowsToContents();
    QStringList head_lab;
    head_lab << "序号" << "设备名" << "描述";
    table_devs->setHorizontalHeaderLabels(head_lab);
    // --------------------------------------------
    // update pcap devs
    on_ui_update_dev_clicked();
    ui->ui_open_dev->setStyleSheet("QPushButton { background-color: red }");
    open_dev_state = false; //default is not open
    // --------------------------------------------
    // muti thread run
    run_npcap_thread = new run_npcap;
    connect(this,&npcap_monitor::npcap_trig,run_npcap_thread,&run_npcap::npcap_sync);
    connect(run_npcap_thread,&run_npcap::packet_trig,this,&npcap_monitor::packet_sync);
    // --------------------------------------------
    // init win
    ui->tabWidget->setCurrentIndex(0);
}

npcap_monitor::~npcap_monitor()
{
    delete ui;
}

void npcap_monitor::on_ui_update_dev_clicked()
{
    // --------------------------------------------
    // pcap init
    if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
    {
        qDebug() << "Error in pcap_findalldevs_ex: " << errbuf;
        exit(1);
    }
    /* Print the list */
    int i = 0;
    for (d = alldevs; d != NULL; d = d->next)
    {
        for(int j = 0; j < 3; j++)
        {
            if(j == 0)
            {
                QTableWidgetItem *item = new QTableWidgetItem;
                item->setText(QString::number(i));
                table_devs->setItem(i,j,item);
            }
            else if(j == 1)
            {
                QTableWidgetItem *item = new QTableWidgetItem;
                item->setText(d->name);
                table_devs->setItem(i,j,item);
            }
            else if(j == 2)
            {
                //qDebug() << d->name;
                if (d->description)
                {
                    QTableWidgetItem *item = new QTableWidgetItem;
                    //qDebug() << d->description;
                    item->setText(d->description);
                    table_devs->setItem(i,j,item);
                }
                else
                {
                    QTableWidgetItem *item = new QTableWidgetItem;
                    item->setText("NULL description");
                    table_devs->setItem(i,j,item);
                    //qDebug() << "(No description available)\n";
                }
            }
        }

        i++;
    }
    if (i == 0)
    {
        qDebug() << "\nNo interfaces found! Make sure Npcap is installed.\n";
    }
    /* We don't need any more the device list. Free it */
    pcap_freealldevs(alldevs);
    on_ui_table_cellClicked(0,0);
}


void npcap_monitor::on_ui_open_dev_clicked()
{
    //on_ui_close_cap_clicked();
    /* Open the adapter */
    if (d->name == NULL)
    {
        return;
    }
    QByteArray ba(d->name);
    QString txt(ba);
    if ((adhandle = pcap_open_live(d->name,	// name of the device
                             65536,			// portion of the packet to capture.
                                            // 65536 grants that the whole packet will be captured on all the MACs.
                             1,				// promiscuous mode (nonzero means promiscuous)
                             1000,			// read timeout
                             errbuf			// error buffer
                             )) == NULL)
    {
        //fprintf(stderr,"\nUnable to open the adapter. %s is not supported by Npcap\n", d->name);
        qDebug() << "\nUnable to open the adapter" << d->name << " is not supported by Npcap\n";
        ui->ui_raw_data->append("open:" + txt + " fail\n");
        open_dev_state = false;
        ui->ui_open_dev->setStyleSheet("QPushButton { background-color: red }");
    }
    else
    {
        ui->ui_raw_data->append("open:" + txt + " ok\n");
        ui->ui_open_dev->setStyleSheet("QPushButton { background-color: green }");
        open_dev_state = true;
        pcap_close(adhandle);
    }

}




void npcap_monitor::on_ui_start_cap_clicked()
{
    if(open_dev_state == false)
    {
        qDebug() << "open error";
        return;
    }
    //this loop is too hard to control
    //pcap_loop(adhandle, 0, packet_handler, NULL);
    emit npcap_trig(d->name);
    run_npcap_thread->start();
}


void npcap_monitor::on_ui_close_cap_clicked()
{
    run_npcap_thread->set_pcap_enable(false);
    run_npcap_thread->wait();
    run_npcap_thread->quit();
    open_dev_state = false;
    ui->ui_open_dev->setStyleSheet("QPushButton { background-color: red }");
    ui->ui_data_show->clear();
}

void npcap_monitor::packet_handler(u_char *param, const pcap_pkthdr *header, const u_char *pkt_data)
{
    struct tm *ltime;
    char timestr[16];
    time_t local_tv_sec;

    /* convert the timestamp to readable format */
    local_tv_sec = header->ts.tv_sec;
    ltime=localtime(&local_tv_sec);
    strftime( timestr, sizeof timestr, "%H:%M:%S", ltime);

    qDebug() << timestr << ":"
             << header->ts.tv_usec  << ":"
             << header->len  << ":"
             << param  << ":"
             << pkt_data;

}

void npcap_monitor::on_ui_table_cellClicked(int row, int column)
{
    for (int row = 0; row < table_devs->rowCount(); ++row)
    {
        for (int column = 0; column < table_devs->columnCount(); ++column)
        {
            QTableWidgetItem *item = table_devs->item(row, column);
            if (item)
            {
                item->setBackgroundColor(Qt::transparent); // default color
            }
        }
    }
    table_devs->item(row,1)->setBackgroundColor(Qt::green);
    int i;
    for(d=alldevs, i=0; i< row ;d=d->next, i++);
    //unused
    (void)(column);
}

void npcap_monitor::packet_sync(char *pkg_time, const pcap_pkthdr *header, const u_char *pkt_data)
{
    qDebug() << pkg_time << ":"
             << header->ts.tv_usec  << ":"
             << header->len  << ":"
             << pkt_data;

    QByteArray ba((const char *)pkt_data,header->len);
    QDataStream out(&ba,QIODevice::ReadWrite);
    QString buf;
    while(!out.atEnd())
    {
        qint8 outChar = 0;
        out >> outChar;
        QString str = QString("%1").arg(outChar&0xFF,2,16,QLatin1Char('0')).toUpper() + QString(" ");   //2 byte
        buf += str;
    }
    QString time(pkg_time);
    QString txt = "[" + time + "](" + QString::number(header->len) + ")" + buf + "\n";
    ui->ui_data_show->append(txt);

}

// ###########################################
// multi thread run class
run_npcap::run_npcap(QObject *parent) : QThread(parent)
{

}
void run_npcap::set_pcap_enable(bool en)
{
    m_lock.lockForWrite();
    pcap_enable = en;
    m_lock.unlock();
}
void run_npcap::run()
{
    m_lock.lockForWrite();
    pcap_enable = true;
    m_lock.unlock();
    m_lock.lockForRead();
    while(pcap_enable)
    {
        m_lock.unlock();
        if(pcap_next_ex(adhandle, &header, &pkt_data) <= 0)
        {
            continue; //get error
        }
        struct tm *ltime;
        char timestr[16];
        time_t local_tv_sec;
        /* convert the timestamp to readable format */
        local_tv_sec = header->ts.tv_sec;
        ltime=localtime(&local_tv_sec);
        strftime( timestr, sizeof timestr, "%H:%M:%S", ltime);

        qDebug() << timestr << ":"
                 << header->ts.tv_usec  << ":"
                 << header->len  << ":"
                 << pkt_data;
        pkg_time = timestr;
        emit packet_trig(pkg_time,header,pkt_data);
        m_lock.lockForRead();
    }
    m_lock.unlock();
    pcap_close(adhandle);
}

void run_npcap::npcap_sync(const char * d_name)
{

    if ((adhandle = pcap_open_live(d_name,	// name of the device
                             65536,			// portion of the packet to capture.
                                            // 65536 grants that the whole packet will be captured on all the MACs.
                             1,				// promiscuous mode (nonzero means promiscuous)
                             1000,			// read timeout
                             errbuf			// error buffer
                             )) == NULL)
    {
        qDebug() << "\nUnable to open the adapter" << d_name << " is not supported by Npcap\n";
    }

}


